﻿using System;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

namespace MFGLib
{
	/// <summary>
	/// 单实例
	/// </summary>
	public sealed class FrameSingleton
	{
		/// <summary>
		/// 用以判断某个窗口是否为前一个进程主窗口的回调
		/// </summary>
		/// <param name="hwnd">窗口句柄</param>
		/// <returns>如果是前一个进程的主窗口返回true，否则返回false</returns>
		public delegate bool CheckHwndDelegate(IntPtr hwnd);

		/// <summary>
		/// 唯一表示符
		/// </summary>
		public string GUID { get; private set; }

		/// <summary>
		/// 寻找到的前一个进程主窗口
		/// </summary>
		public IntPtr PrevHwnd { get; private set; }
		
		private Mutex m_mutext = null;		
		private string m_titlePattern = null;
		private string m_classPattern = null;
		private CheckHwndDelegate m_func = null;

		/// <summary>
		/// 构造函数
		/// </summary>
		/// <param name="guid">唯一标识</param>
		public FrameSingleton(string guid)
		{
			GUID = guid;
		}		

		/// <summary>
		/// 尝试创建单实例标识
		/// </summary>
		/// <returns>创建成功返回true，失败返回false</returns>
		public bool Create()
		{
			if (string.IsNullOrEmpty(GUID))
			{
				throw new Exception("Invalid GUID format.");
			}

			bool createdNew;
			m_mutext = new Mutex(true, GUID, out createdNew);
			return createdNew;
		}

		/// <summary>
		/// 尝试创建单实例标识，如果失败则寻找前一个进程主窗口并存放于PrevHwnd
		/// </summary>
		/// <param name="func">用以判断某个窗口是否为前一个进程主窗口的回调</param>
		/// <returns>创建成功返回true，失败返回false</returns>
		public bool Create(CheckHwndDelegate func)
		{
			if (func == null)
			{
				throw new ArgumentNullException("func");
			}

			if (Create())
			{
				return true;
			}

			m_func = func;
			PrevHwnd = IntPtr.Zero;
			EnumWindows(EnumWindowsProcByFunc, 0);
			return false;
		}

		/// <summary>
		/// 尝试创建单实例标识，如果失败则寻找前一个进程主窗口并存放于PrevHwnd
		/// </summary>
		/// <param name="titlePattern">窗口标题匹配正则，null则不判断</param>
		/// <param name="classPattern">窗口类名匹配正则，null则不判断</param>
		/// <returns>创建成功返回true，失败返回false</returns>
		public bool Create(string titlePattern, string classPattern = null)
		{
			if (titlePattern == null && classPattern == null)
			{
				throw new Exception("At least one of titlePattern and classPattern must be specified.");
			}

			if (Create())
			{
				return true;
			}

			m_titlePattern = titlePattern;
			m_classPattern = classPattern;
			PrevHwnd = IntPtr.Zero;
			EnumWindows(EnumWindowsProcByText, 0);
			return false;
		}

		/// <summary>
		/// 激活前一个进程主窗口
		/// </summary>
		public void ActivatePrevHwnd()
		{
			if (PrevHwnd == IntPtr.Zero)
			{
				return;
			}

			if (GetForegroundWindow() != PrevHwnd)
			{
				if (IsIconic(PrevHwnd))
				{
					ShowWindow(PrevHwnd, 9); // SW_RESTORE
				}
				else if (!IsWindowVisible(PrevHwnd))
				{
					ShowWindow(PrevHwnd, 5); // SW_SHOW
				}

				SetForegroundWindow(PrevHwnd);
			}
		}

		bool EnumWindowsProcByFunc(IntPtr hwnd, int lParam)
		{
			if (m_func.Invoke(hwnd))
			{
				PrevHwnd = hwnd;
				return false;
			}

			return true;
		}

		bool EnumWindowsProcByText(IntPtr hwnd, int lParam)
		{
			if (!string.IsNullOrEmpty(m_titlePattern))
			{
				string text = GetWindowText(hwnd);
				if (text != null && Regex.IsMatch(text, m_titlePattern))
				{
					PrevHwnd = hwnd;
					return false;
				}
			}

			if (!string.IsNullOrEmpty(m_classPattern))
			{
				string text = GetWindowClass(hwnd);
				if (text != null && Regex.IsMatch(text, m_classPattern))
				{
					PrevHwnd = hwnd;
					return false;
				}
			}			

			return true;
		}

		static string GetWindowText(IntPtr hwnd)
		{
			StringBuilder sb = new StringBuilder(1000);
			if (!GetWindowText(hwnd, sb, sb.Capacity))
			{
				return null;
			}

			return sb.ToString();
		}

		static string GetWindowClass(IntPtr hWnd)
		{
			StringBuilder sb = new StringBuilder(255);
			if (!GetClassName(hWnd, sb, sb.Capacity))
			{
				return null;
			}

			return sb.ToString();
		}

		[DllImport("user32.dll")]
		extern static bool IsWindow(IntPtr hwnd);

		[DllImport("user32.dll")]
		static extern bool IsWindowVisible(IntPtr hwnd);

		[DllImport("user32.dll")]
		static extern bool IsIconic(IntPtr hwnd);

		[DllImport("user32.dll")]
		static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);

		[DllImport("user32.dll")]
		static extern IntPtr GetForegroundWindow();

		[DllImport("user32.dll")]
		static extern bool SetForegroundWindow(IntPtr hwnd);

		delegate bool EnumWindowsCallBack(IntPtr hwnd, int lParam);

		[DllImport("user32.dll")]
		static extern bool EnumWindows(EnumWindowsCallBack callback, int lParam);

		[DllImport("user32.dll")]
		static extern bool GetWindowText(IntPtr hWnd, StringBuilder text, int nMaxCount);

		[DllImport("user32.dll")]
		static extern bool GetClassName(IntPtr hWnd, StringBuilder text, int nMaxCount);		
	}
}
